import SparkMD5 from 'spark-md5';
import type {UploadProgressEvent} from "element-plus";

/**
 * 根据文件大小智能计算分片大小和分片数量
 * @param file - 需要上传的文件对象
 * @returns 返回包含分片大小和分片数量的对象
 */
export function calculatePartSize(file: File): { partSize: number, partCount: number } {
  // 定义最小、最大分片大小
  const MIN_PART_SIZE = 20 * 1024 * 1024; // 20MB
  const MAX_PART_SIZE = 64 * 1024 * 1024; // 64MB

  const fileSize = file.size;
  let partSize = MIN_PART_SIZE; // 默认最小分片

  // 根据文件大小调整分片
  if (fileSize >= 1024 * 1024 * 1024) {
    // 大于 1GB -> 64MB
    partSize = MAX_PART_SIZE;
  } else if (fileSize >= 500 * 1024 * 1024) {
    // 大于 500MB -> 50MB
    partSize = 50 * 1024 * 1024;
  } else if (fileSize >= 200 * 1024 * 1024) {
    // 大于 200MB -> 30MB
    partSize = 30 * 1024 * 1024;
  }

  const partCount = Math.ceil(fileSize / partSize);
  return {partSize, partCount};
}

/**
 * 计算文件的 MD5 哈希值
 * @param file - 需要计算的文件对象
 * @param onProgress - 计算进度回调
 * @param abortSignal - 用于取消操作的信号
 */
export function calculateFileMD5(
  file: File,
  onProgress?: (evt: UploadProgressEvent) => void,
  abortSignal?: AbortSignal
): Promise<string> {
  return new Promise<string>((resolve, reject) => {
    const {partSize, partCount} = calculatePartSize(file);
    const md5Hasher = new SparkMD5.ArrayBuffer();
    const fileReader = new FileReader();
    let currentPart = 0;

    // 如果操作被取消，直接中止
    if (abortSignal?.aborted) {
      fileReader.abort();
      reject(new DOMException("Operation aborted", "AbortError"));
      return;
    }

    // 监听取消信号
    abortSignal?.addEventListener("abort", () => fileReader.abort());

    // 成功读取分片后的回调
    fileReader.onload = (event: ProgressEvent<FileReader>) => {
      if (abortSignal?.aborted) return;

      const arrayBuffer = event.target?.result as ArrayBuffer;
      if (arrayBuffer) {
        md5Hasher.append(arrayBuffer);
      }

      updateProgress();

      currentPart++;
      if (currentPart < partCount) {
        readNextPart();
      } else {
        resolve(md5Hasher.end());
      }
    };

    // 读取错误时的回调
    fileReader.onerror = reject;

    // 读取下一个分片
    const readNextPart = () => {
      if (abortSignal?.aborted) return;

      const start = currentPart * partSize;
      const end = Math.min(start + partSize, file.size);
      const fileChunk = file.slice(start, end);
      fileReader.readAsArrayBuffer(fileChunk);
    };

    /**
     * 更新计算进度
     */
    const updateProgress = () => {
      const processedSize = Math.min((currentPart + 1) * partSize, file.size);
      const percent = (processedSize / file.size) * 100;
      onProgress?.({loaded: processedSize, total: file.size, percent} as UploadProgressEvent);
    };

    // 开始读取第一个分片
    readNextPart();
  });
}
